package lol.clann.bukkit.pluginboot.model.gui;

import com.fasterxml.jackson.annotation.JsonIgnore;
import lol.clann.bukkit.pluginboot.model.IndexedWrapper;
import lol.clann.bukkit.pluginboot.model.ItemStackBuilder;
import lol.clann.bukkit.pluginboot.util.Validator;
import lol.clann.clannboot.annotation.inject.Inject;
import lol.clann.clannboot.context.BeanContext;
import lol.clann.clannboot.util.collection.CollectionUtils;
import org.bukkit.Material;
import org.bukkit.entity.Player;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Stream;

/**
 * @author pyz
 * @date 2018/7/1 上午9:45
 */
public class Page<S extends IconSupplier> {
    protected String title;
    protected int row = 6;
    protected int column = 9;
    protected int contentCapacity = 45;
    protected int buttonCapacity = 9;
    protected int guiCapacity = 54;
    protected String pluginId;
    @Inject
    @JsonIgnore
    protected Validator validator;
    private List<View> views = new ArrayList<>();
    private List<Button> buttons = new ArrayList<>(9);
    private int size = 0;

    public Page(String pluginId, String title) {
        BeanContext.inject(this);
        this.title = title;
        this.pluginId = pluginId;
        CollectionUtils.fill(buttons, null, 9);
        buttons.set(0,
                new Button()
                        .setIcon(ItemStackBuilder.builder()
                                .material(Material.DIAMOND)
                                .quantity(1)
                                .addLore("§a上一页")
                                .build())
        );
        buttons.set(8,
                new Button()
                        .setIcon(ItemStackBuilder.builder()
                                .material(Material.DIAMOND)
                                .quantity(1)
                                .addLore("§a下一页")
                                .build())
        );
        addPage();
    }

    /**
     * 遍历存储区
     *
     * @return
     */
    public Stream<IndexedWrapper<Element<S>>> stream() {
        List<Stream<IndexedWrapper<Element<S>>>> streams = new ArrayList<>();
        for (int i = 0; i < views.size(); i++) {
            int page = i;
            Stream<IndexedWrapper<Element<? extends IconSupplier>>> stream = views.get(i).stream();
            stream = stream.filter(iw -> iw.getIndex() < getContentCapacity()).map(iw -> iw.setIndex(iw.getIndex() + page * getContentCapacity()));
            streams.add((Stream) stream);
        }
        return streams.stream().flatMap(Function.identity());
    }

    /**
     * 正向查找第一个空格子
     *
     * @return
     */
    public int leftFirstEmptySlotIndex() {
        for (int i = 0; i < views.size(); i++) {
            int index = views.get(i).leftFirstEmptySlotIndex();
            if (index != -1) {
                return i * guiCapacity + index;
            }
        }
        return -1;
    }

    /**
     * 逆向查找第一个空格子
     *
     * @return
     */
    public int rightFirstEmptySlotIndex() {
        for (int i = views.size() - 1; i >= 0; i--) {
            int index = views.get(i).rightFirstEmptySlotIndex();
            if (index != -1) {
                return i * guiCapacity + index;
            }
        }
        return -1;
    }

    /**
     * 这是一个空格子
     * 这个格子之后全是空格子
     * 满足上述条件的第一个格子
     * 如果页已满,则范围页的最后一个格子序号+1
     *
     * @return
     */
    public int appendEmptySlotIndex() {
        int maxIndex = views.size() * getContentCapacity() - 1;
        int i = maxIndex;
        for (; i >= 0; i--) {
            if (getContent(i) != null) {
                return i + 1;
            }
        }
        return 0;
    }

    /**
     * 清除所有页的内容
     * 并删除所有页
     * 在新建一页
     * 当需要刷新玩家已打开的gui时,不能使用此方法
     */
    public void clear() {
        for (View view : views) {
            view.clear();
        }
        views.clear();
        addPage();
        size = 0;
    }

    /**
     * 清除所有页的内容
     */
    public void clearContent() {
        for (View view : views) {
            view.clear();
        }
        size = 0;
    }

    /**
     * 移除空页
     */
    public void clearEmptyPage() {
        if (views.size() <= 1) {
            // 至少保留一页
            return;
        }
        for (int i = views.size() - 1; i >= 0; i--) {
            if (isEmpty(views.get(i))) {
                views.remove(i);
            }
        }
        for (int i = 0; i < views.size(); i++) {
            views.get(i).setPage(i);
        }
    }

    /**
     * 检查指定分页是否是空的
     *
     * @param view
     * @return
     */
    private boolean isEmpty(View view) {
        for (int i = 0; i < getContentCapacity(); i++) {
            Element ele = view.getElement(i);
            if (ele != null && ele.getExtend() != null) {
                return false;
            }
        }
        return true;
    }

    public int size() {
        return size;
    }

    public boolean isEmpty() {
        return size() == 0;
    }


    /**
     * 确保存储区指定序号的格子存在
     *
     * @param index
     */
    public void ensureCapacity(int index) {
        int page = index / getContentCapacity() + 1;
        while (views.size() < page) {
            addPage();
        }
    }

    public void setPre(Button pre) {
        pre.setClickAction(action -> {
            open(action.getPlayer(), action.getView().getPage() - 1);
            return false;
        });
        buttons.set(0, pre);
        syncButton();
    }

    public void setNext(Button next) {
        next.setClickAction(action -> {
            open(action.getPlayer(), action.getView().getPage() + 1);
            return false;
        });
        buttons.set(8, next);
        syncButton();
    }

    public void sync() {
        for (View view : views) {
            view.sync();
        }
        //移除后面的空页
        while (views.get(views.size() - 1).isEmpty() && views.size() > 1) {
            removePage();
        }
    }

    /**
     * 更改了按钮设定后,需要调用此方法
     */
    private void syncButton() {
        for (int i = 0; i < buttons.size(); i++) {
            for (View view : views) {
                view.setElement(i + getContentCapacity(), buttons.get(i));
            }
        }
        //去掉第一页的上一页按钮和最后一页的下一页按钮
        views.get(0).setElement(getContentCapacity(), null);
        views.get(views.size() - 1).setElement(getGuiCapacity() - 1, null);
    }

    public Element<S> getContent(int index) {
        int page = index / getContentCapacity();
        index = index % getContentCapacity();
        return views.get(page).getElement(index);
    }

    public Element<S> setContent(int index, Element<S> element) {
        ensureCapacity(index);
        int page = index / getContentCapacity();
        index = index % getContentCapacity();
        Element<S> replaced = views.get(page).setElement(index, element);
        sync();
        if (replaced != null) {
            size--;
        }
        if (element != null) {
            size++;
        }
        return replaced;
    }

    /**
     * @param index [1,8)
     * @return
     */
    public Button getButton(int index) {
        validator.exclusiveBetween(0, 8, index);
        return (Button) views.get(0).getElement(index + getContentCapacity());
    }

    public Button setButton(int index, Button element) {
        validator.exclusiveBetween(0, 8, index);
        buttons.set(index, element);
        Button replaced = getButton(index);
        for (View view : views) {
            view.setElement(index + getContentCapacity(), element);
        }
        syncButton();
        return replaced;
    }

    public int getContentCapacity() {
        return contentCapacity;
    }

    public int getButtonCapacity() {
        return buttonCapacity;
    }

    public int getGuiCapacity() {
        return getButtonCapacity() + getContentCapacity();
    }

    public int getRow() {
        return row;
    }

    public int getColumn() {
        return column;
    }

    public String getTitle() {
        return title;
    }

    public void open(Player player, int page) {
        views.get(page).open(player);
    }

    private void addPage() {
        View<S> view = new View<>(pluginId, title);
        view.setPage(views.size());
        views.add(view);
        syncButton();
    }

    private void removePage() {
        if (views.size() <= 1) {
            throw new RuntimeException("最少保留一页");
        }
        View view = views.remove(views.size() - 1);
        view.clear();
        syncButton();
    }

    private boolean isEmpty(int page) {
        View view = views.get(page);
        int size = view.size() - buttons.size();
        //第一页没有上一页按钮
        if (page == 0) {
            size++;
        }
        //最后一页没有下一页按钮
        if (page == views.size() - 1) {
            size++;
        }
        return size == 0;
    }
}
